package com.nageoffer.shortlink.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.nageoffer.shortlink.gateway.config.Config;
import com.nageoffer.shortlink.gateway.dto.GatewayErrorResult;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

// 继承AbstractGatewayFilterFactory并指定配置类为Config
@Component
public class TokenValidateGatewayFilterFactory extends AbstractGatewayFilterFactory<Config> {

    // 声明Redis操作模板（用于存取用户token）
    private final StringRedisTemplate stringRedisTemplate;

    // 构造函数注入StringRedisTemplate
    public TokenValidateGatewayFilterFactory(StringRedisTemplate stringRedisTemplate) {
        super(Config.class);
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    // 实现过滤器核心逻辑
    public GatewayFilter apply(Config config) {
        // 返回GatewayFilter实例
        return (exchange, chain) -> {
            // 获取当前HTTP请求对象
            ServerHttpRequest request = exchange.getRequest();
            // 获取请求路径（如"/api/short-link/admin"）
            String requestPath = request.getPath().toString();
            // 获取请求方法（如GET/POST）
            String requestMethod = request.getMethod().name();

            // 检查当前路径是否在白名单中
            if (!isPathInWhiteList(requestPath, requestMethod, config.getWhitePathList())) {
                // 从请求头获取用户名和token
                String username = request.getHeaders().getFirst("username");
                String token = request.getHeaders().getFirst("token");
                Object userInfo;

                // 校验用户名和token是否有效
                if (StringUtils.hasText(username) && StringUtils.hasText(token) &&
                        (userInfo = stringRedisTemplate.opsForHash().get("short-link:login:" + username, token)) != null) {
                    // 将Redis中的用户信息转为JSON对象
                    JSONObject userInfoJsonObject = JSON.parseObject(userInfo.toString());
                    // 构建新的请求头（添加用户ID和真实姓名）
                    ServerHttpRequest.Builder builder = exchange.getRequest().mutate().headers(httpHeaders -> {
                        httpHeaders.set("userId", userInfoJsonObject.getString("id"));
                        httpHeaders.set("realName", URLEncoder.encode(userInfoJsonObject.getString("realName"), StandardCharsets.UTF_8));
                    });
                    // 放行请求（携带新请求头）
                    return chain.filter(exchange.mutate().request(builder.build()).build());
                }

                // 若校验失败，准备错误响应
                ServerHttpResponse response = exchange.getResponse();
                // 设置HTTP状态码为401（未授权）
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                // 返回JSON格式的错误信息
                return response.writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    // 构建错误结果对象
                    GatewayErrorResult resultMessage = GatewayErrorResult.builder()
                            .status(HttpStatus.UNAUTHORIZED.value())
                            .message("Token validation error")
                            .build();
                    // 将错误信息转为字节流
                    return bufferFactory.wrap(JSON.toJSONString(resultMessage).getBytes());
                }));
            }
            // 白名单路径直接放行
                    return chain.filter(exchange);
        };
    }

    // 检查请求路径是否在白名单中
    private boolean isPathInWhiteList(String requestPath, String requestMethod, List<String> whitePathList) {
        // 白名单规则：1.路径匹配白名单前缀 或 2.特定路径+POST方法
        return (!CollectionUtils.isEmpty(whitePathList) && whitePathList.stream().anyMatch(requestPath::startsWith))
                || (Objects.equals(requestPath, "/api/short-link/admin/v1/user") && Objects.equals(requestMethod, "POST"));
    }
}